package com.roamcattery.plugins.load;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.roamcattery.plugins.entity.CustomizeConfig;
import com.roamcattery.plugins.entity.Generator;
import com.roamcattery.plugins.entity.OtherFile;
import com.roamcattery.plugins.util.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @author wangwc@wumart.com
 * @version v1.0.0
 * @date 2021/6/7 10:12
 */
@Slf4j
public class YamlPropertiesParser implements PropertiesLoadParser{

    private String property_name = "application-generator.yml";
    private static final String split_1 = "-";
    private static final String split_2 = "_";

    private ConcurrentMap<String, Object> properties = new ConcurrentHashMap<String, Object>();

    @Override
    public Generator load(String resourcePath) {
        File file = new File(resourcePath);
        return this.load(file);
    }


    public Generator load(File file) {
        try {
            Yaml yaml = new Yaml();
            if (file != null) {
                //获取test.yaml文件中的配置数据，然后转换为obj，
                //也可以将值转换为Map
                Map<String, Object> map = yaml.load(new FileInputStream(file));
                log.info("获取yml配置-----------------------------------------");
                //通过map我们取值就可以了.
                map  = toUp(map);
                JSONObject json = new JSONObject((LinkedHashMap)map.get("generator"));
                Generator generator = new Generator();

                JSONObject dataSourceJson = json.getJSONObject("dataSourceConfig");
                DataSourceConfig dataSourceConfig = this.dataSourceConvert(dataSourceJson);
                generator.setDataSourceConfig(dataSourceConfig);
                JSONObject global = json.getJSONObject("globalConfig");
                GlobalConfig globalConfig = globalConvert(global);
                generator.setGlobalConfig(globalConfig);
                PackageConfig packageConfig = packageConvert(json.getJSONObject("packageConfig"));
                generator.setPackageConfig(packageConfig);
                JSONObject strategyJson = json.getJSONObject("strategyConfig");
                StrategyConfig strategyConfig = strategyConvert(strategyJson);
                generator.setStrategyConfig(strategyConfig);
                JSONObject customizeJson = json.getJSONObject("customizeConfig");
                if (customizeJson !=null){
                    CustomizeConfig customizeConfig = customizeConvert(customizeJson);
                    generator.setCustomizeConfig(customizeConfig);
                }
                return generator;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private DataSourceConfig dataSourceConvert(JSONObject json){
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        String url = json.getString("url");
        if (StringUtils.isBlank(url)){
            throw new IllegalArgumentException("url 参数不能为空");
        }
        String driverName = json.getString("driverName");
        if (StringUtils.isBlank(driverName)){
            throw new IllegalArgumentException("driverName 参数不能为空");
        }
        String username = json.getString("username");
        if (StringUtils.isBlank(username)){
            throw new IllegalArgumentException("username 参数不能为空");
        }
        String password = json.getString("password");
        if (StringUtils.isBlank(password)){
            throw new IllegalArgumentException("password 参数不能为空");
        }
        dataSourceConfig.setUrl(url);
        dataSourceConfig.setDriverName(driverName);
        dataSourceConfig.setUsername(username);
        dataSourceConfig.setPassword(password);
        log.info("装载dataSourceConfig配置:");
        log.info("url:{}",url);
        log.info("driverName:{}",driverName);
        log.info("username:{}",username);
        log.info("password:{}",password);
        return dataSourceConfig;
    }

    private GlobalConfig globalConvert(JSONObject json){
        GlobalConfig globalConfig = new GlobalConfig();
        String outputDir = json.getString("outputDir");
        if (StringUtils.isBlank(outputDir)){
            throw new IllegalArgumentException("outPutDir 参数不能为空");
        }
        Boolean fileOverride = json.getBoolean("fileOverride");
        fileOverride = fileOverride == null ? true : fileOverride;
        Boolean open = json.getBoolean("open");
        open = open == null ? true : open;
        Boolean enableCache = json.getBoolean("enableCache");
        enableCache = enableCache == null ? false : enableCache;
        String author = json.getString("author");
        String idType = json.getString("idType");
        IdType id_Type = StringUtils.isBlank(idType) ? IdType.AUTO : IdType.valueOf(idType.toUpperCase());
        String dateType = json.getString("dateType");
        DateType date_Type = StringUtils.isBlank(dateType) ? DateType.ONLY_DATE : DateType.valueOf(dateType.toUpperCase());
        Boolean baseColumnList = json.getBoolean("baseColumnList");
        baseColumnList = baseColumnList == null ? true : baseColumnList;
        Boolean baseResultMap = json.getBoolean("baseResultMap");
        baseResultMap = baseResultMap == null ? true : baseResultMap;
        Boolean swagger2 = json.getBoolean("swagger2");
        swagger2 = swagger2 == null ? false : swagger2;

        globalConfig.setOutputDir(outputDir);
        globalConfig.setFileOverride(fileOverride);
        globalConfig.setOpen(open);
        globalConfig.setEnableCache(enableCache);
        globalConfig.setAuthor(author);
        globalConfig.setIdType(id_Type);
        globalConfig.setDateType(date_Type);
        globalConfig.setBaseColumnList(baseColumnList);
        globalConfig.setBaseResultMap(baseResultMap);
        globalConfig.setSwagger2(swagger2);
        log.info("装载globalConfig配置:");
        log.info("outPutDir[输出目录]:{}",outputDir);
        log.info("fileOverride[文件是否覆盖]:{}",fileOverride);
        log.info("open[文件夹是否打开]:{}",open);
        log.info("enableCache[是否开启二级缓存]:{}",enableCache);
        log.info("author[作者签名]:{}",author);
        log.info("idType[主键生成策略]:{}",id_Type);
        log.info("dateType[时间类型]:{}",date_Type);
        log.info("baseColumnList[Xml是否生成baseColumnList]:{}",baseColumnList);
        log.info("baseResultMap[Xml是否生成baseResultMap]:{}",baseResultMap);
        log.info("swagger2[是否生成swagger2注解]:{}",swagger2);
        return globalConfig;
    }

    private PackageConfig packageConvert(JSONObject json){
        PackageConfig packageConfig = new PackageConfig();
        String parent = json.getString("parent");
        if (StringUtils.isBlank(parent)){
            throw new IllegalArgumentException("parent 参数不能为空");
        }
        String moduleName = json.getString("moduleName");
        if (StringUtils.isBlank(moduleName)){
            throw new IllegalArgumentException("moduleName 参数不能为空");
        }
        String entity = json.getString("entity");
        String mapper = json.getString("mapper");
        String service = json.getString("service");
        String serviceImpl = json.getString("serviceImpl");
        String controller = json.getString("controller");
        log.info("装载packageConfig配置:");
        log.info("parent[父包名]:{}",parent);
        log.info("moduleName[模块名称]:{}",moduleName);
        log.info("entity[实体包名称]:{}",entity);
        log.info("mapper[Mapper包名称]:{}",mapper);
        log.info("service[service包名称]:{}",service);
        log.info("serviceImpl[service实现类包]:{}",serviceImpl);
        log.info("controller[Controller包名称]:{}",controller);

        packageConfig.setParent(parent);
        packageConfig.setModuleName(moduleName);
        packageConfig.setEntity(entity);
        packageConfig.setMapper(mapper);
        packageConfig.setService(service);
        packageConfig.setServiceImpl(serviceImpl);
        packageConfig.setController(controller);
        return packageConfig;
    }

    private StrategyConfig strategyConvert(JSONObject json){
        StrategyConfig strategyConfig = new StrategyConfig();

        String naming = json.getString("naming");
        NamingStrategy namingStrategy = StringUtils.isBlank(naming) ? NamingStrategy.underline_to_camel : NamingStrategy.valueOf(naming);
        String columnNaming = json.getString("columnNaming");
        NamingStrategy columnNamingStrategy = StringUtils.isBlank(columnNaming) ? NamingStrategy.underline_to_camel : NamingStrategy.valueOf(columnNaming);
        Boolean entityLombokModel = json.getBoolean("entityLombokModel");
        entityLombokModel = entityLombokModel == null ? true : entityLombokModel;

        Boolean restControllerStyle = json.getBoolean("restControllerStyle");
        restControllerStyle = restControllerStyle == null ? true : restControllerStyle;

        Boolean controllerMappingHyphenStyle = json.getBoolean("entityLombokModel");
        controllerMappingHyphenStyle = controllerMappingHyphenStyle == null ? true : controllerMappingHyphenStyle;

        String tablePrefix = json.getString("tablePrefix");

        Boolean entityBooleanColumnRemoveIsPrefix = json.getBoolean("entityBooleanColumnRemoveIsPrefix");
        entityBooleanColumnRemoveIsPrefix = entityBooleanColumnRemoveIsPrefix == null ? true : entityBooleanColumnRemoveIsPrefix;

        Boolean entityColumnConstant = json.getBoolean("entityColumnConstant");
        entityColumnConstant = entityColumnConstant == null ? true : entityColumnConstant;

        String include = json.getString("include");

        log.info("装载strategyConfig配置:");
        log.info("naming[配置数据表与实体类名之间映射的策略]:{}",namingStrategy);
        log.info("columnNaming[配置数据表的字段与实体类的属性名之间映射的策略]:{}",columnNamingStrategy);
        log.info("entityLombokModel[配置 lombok 模式]:{}",entityLombokModel);
        log.info("restControllerStyle[配置 rest 风格的控制器（@RestController）]:{}",restControllerStyle);
        log.info("controllerMappingHyphenStyle[配置驼峰转连字符]:{}",controllerMappingHyphenStyle);
        log.info("tablePrefix[配置表前缀，生成实体时去除表前缀]:{}",tablePrefix);
        log.info("entityBooleanColumnRemoveIsPrefix[Boolean类型字段是否移除is前缀处理]:{}",entityBooleanColumnRemoveIsPrefix);
        log.info("entityColumnConstant[配置表字段常量]:{}",entityColumnConstant);
        log.info("include[指定表名]:{}",include);
        strategyConfig.setNaming(namingStrategy);
        strategyConfig.setColumnNaming(columnNamingStrategy);
        strategyConfig.setEntityLombokModel(entityLombokModel);
        strategyConfig.setRestControllerStyle(restControllerStyle);
        strategyConfig.setControllerMappingHyphenStyle(controllerMappingHyphenStyle);
        strategyConfig.setTablePrefix(tablePrefix);
        strategyConfig.setEntityBooleanColumnRemoveIsPrefix(entityBooleanColumnRemoveIsPrefix);
        strategyConfig.setEntityColumnConstant(entityColumnConstant);
        strategyConfig.setInclude(include.split(","));
        return strategyConfig;
    }

    private CustomizeConfig customizeConvert(JSONObject json){
        CustomizeConfig customizeConfig = new CustomizeConfig();
        //xml文件生成磁盘地址
        String xmlFileOutPath = json.getString("xmlFileOutPath");
        //xml模板地址
        String xmlTemplatePath = json.getString("xmlTemplatePath");
        String reqTemplatePath = json.getString("reqTemplatePath");
        String reqPackage = json.getString("reqPackage");
        String reqSuffix = json.getString("reqSuffix");
        String respTemplatePath = json.getString("respTemplatePath");
        String respPackage = json.getString("respPackage");
        String respSuffix = json.getString("respSuffix");
        String bizService = json.getString("bizService");
        String bizServiceTemplate = json.getString("bizServiceTemplate");
        String bizServiceImpl = json.getString("bizServiceImpl");
        String bizServiceImplTemplate = json.getString("bizServiceImplTemplate");
        JSONArray fileJson = json.getJSONArray("fileMap");
        if (CollectionUtils.isNotEmpty(fileJson)){
            List<OtherFile> fileMap = fileJson.toJavaList(OtherFile.class);
            customizeConfig.setFileMap(fileMap);
        }

        customizeConfig.setXmlFileOutPath(xmlFileOutPath);
        customizeConfig.setXmlTemplatePath(xmlTemplatePath);
       /* customizeConfig.setReqTemplatePath(reqTemplatePath);
        customizeConfig.setReqPackage(reqPackage);
        customizeConfig.setReqSuffix(reqSuffix);
        customizeConfig.setRespTemplatePath(respTemplatePath);
        customizeConfig.setRespPackage(respPackage);
        customizeConfig.setRespSuffix(respSuffix);
        customizeConfig.setBizService(bizService);
        customizeConfig.setBizServiceTemplate(bizServiceTemplate);
        customizeConfig.setBizServiceImpl(bizServiceImpl);
        customizeConfig.setBizServiceImplTemplate(bizServiceImplTemplate);*/
        return customizeConfig;
    }

    public Generator load() {
        try {
            Yaml yaml = new Yaml();
            URL url = YamlPropertiesParser.class.getClassLoader().getResource(property_name);
            if (url != null) {
                //获取test.yaml文件中的配置数据，然后转换为obj，
                Object obj = yaml.load(new FileInputStream(url.getFile()));
                System.out.println(obj);
                //也可以将值转换为Map
                Map<String, Object> map = yaml.load(new FileInputStream(url.getFile()));
                log.info("获取yml配置-----------------------------------------");
                //通过map我们取值就可以了.
                map  = toUp(map);
                JSONObject json = new JSONObject((LinkedHashMap)map.get("generator"));
                Generator generator = new Generator();

                JSONObject dataSourceJson = json.getJSONObject("dataSourceConfig");
                DataSourceConfig dataSourceConfig = this.dataSourceConvert(dataSourceJson);
                generator.setDataSourceConfig(dataSourceConfig);
                JSONObject global = json.getJSONObject("globalConfig");
                GlobalConfig globalConfig = globalConvert(global);
                generator.setGlobalConfig(globalConfig);
                PackageConfig packageConfig = packageConvert(json.getJSONObject("packageConfig"));
                generator.setPackageConfig(packageConfig);
                JSONObject strategyJson = json.getJSONObject("strategyConfig");
                StrategyConfig strategyConfig = strategyConvert(strategyJson);
                generator.setStrategyConfig(strategyConfig);
                JSONObject customizeJson = json.getJSONObject("customizeConfig");
                if (customizeJson !=null){
                    CustomizeConfig customizeConfig = customizeConvert(customizeJson);
                    generator.setCustomizeConfig(customizeConfig);
                }
                return generator;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    LinkedHashMap<String,Object> toUp(Map<String,Object> map){
        LinkedHashMap<String,Object> newMap = new LinkedHashMap<String, Object>();
        for (Map.Entry<String,Object> e:map.entrySet()) {
            String key = underlineToCamel(e.getKey(),split_1);
            key = underlineToCamel(key,split_2);
            Object value;
            if (e.getValue() instanceof LinkedHashMap){
                value = toUp((LinkedHashMap<String,Object>)e.getValue());
            }else{
                value = e.getValue();
                log.info(e.getKey() + ":"+e.getValue());
            }
            newMap.put(key,value);
        }
        return newMap;
    }

    public ConcurrentMap<String, Object> getProperties() {
        if (CollectionUtils.isEmpty(properties)) {
            this.load();
        }
        return properties;
    }

    public static String camelToUnderline(String param) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append(split_1);
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString().trim();
    }

    public static String underlineToCamel(String param,String split) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        StringBuilder sb = new StringBuilder(param);
        Matcher mc = Pattern.compile(split).matcher(param);
        int i = 0;
        while (mc.find()) {
            int position = mc.end() - (i++);
            sb.replace(position - 1, position + 1, sb.substring(position, position + 1).toUpperCase());
        }
        return sb.toString().trim();
    }



}
